#include "SDL/SDL.h"
#include "math.h"
#include <iostream>
#include "functions.h"

#define FRAMES_PER_SECOND 40
#define SCREEN_W 800
#define SCREEN_H 400

using namespace std;
SDL_Surface *screen;

//The timer
class Timer
{
    private:
    //The clock time when the timer started
    int startTicks;

    //The ticks stored when the timer was paused
    int pausedTicks;

    //The timer status
    bool paused;

    bool started;

    public:
    //Initializes variables
    Timer();

    //The various clock actions
    void start();
    void stop();
    void pause();
    void unpause();

    //Gets the timer's time
    int get_ticks();

    //Checks the status of the timer
    bool is_started();
    bool is_paused();
};

Timer::Timer()
{
    //Initialize the variables
    startTicks = 0;
    pausedTicks = 0;
    paused = false;
    started = false;
}

void Timer::start()
{
    //Start the timer
    started = true;

    //Unpause the timer
    paused = false;

    //Get the current clock time
    startTicks = SDL_GetTicks();
}

void Timer::stop()
{
    //Stop the timer
    started = false;

    //Unpause the timer
    paused = false;
}

void Timer::pause()
{
    //If the timer is running and isn't already paused
    if( ( started == true ) && ( paused == false ) )
    {
        //Pause the timer
        paused = true;

        //Calculate the paused ticks
        pausedTicks = SDL_GetTicks() - startTicks;
    }
}

void Timer::unpause()
{
    //If the timer is paused
    if( paused == true )
    {
        //Unpause the timer
        paused = false;

        //Reset the starting ticks
        startTicks = SDL_GetTicks() - pausedTicks;

        //Reset the paused ticks
        pausedTicks = 0;
    }
}

int Timer::get_ticks()
{
    //If the timer is running
    if( started == true )
    {
        //If the timer is paused
        if( paused == true )
        {
            //Return the number of ticks when the timer was paused
            return pausedTicks;
        }
        else
        {
            //Return the current time minus the start time
            return SDL_GetTicks() - startTicks;
        }
    }

    //If the timer isn't running
    return 0;
}

bool Timer::is_started()
{
    return started;
}

bool Timer::is_paused()
{
    return paused;
}

// End Class Timer
//

/////
// Genera la parte est�tica del Fondo de acuerdo a una imagen seleccionada
////
// Funcion para hacer en Assembly

extern "C" void copiar ( Uint8* img1, int screen_w, int screen_h, Uint8* img2);
extern "C" void copiarInvertido ( Uint8* img1, int screen_w, int screen_h, Uint8* img2);
extern "C" void diagonal(Uint8* foto, Uint32 w, Uint32 r);
extern "C" void diagonalInvertida(Uint8* foto, Uint32 w, Uint32 r);
extern "C" void zoom(Uint8* foto, Uint32 w, Uint32 h, Uint8* zoom);





/////
// Variables para Generar el Efecto de Plasma en el cielo
int colores[512];
Uint16	vel_vertical_0 = 0, 
		vel_vertical_1 = 0, 
		vel_horizontal_0 = 0, 
		vel_horizontal_1 = 0, 
		vertical_0, 
		vertical_1, 
		horizontal_0, 
		horizontal_1;
/////////

/// Funciones para el fondo de Plasma
/////
// Inicializa la Paleta de colores
/////
void init(int a)
{
	int i;
	float rad;
 
	/*create sin lookup table */
	for (i = 0; i < 512; i++)
    {
		rad =  ((float)i * 0.703125) * 0.0174532;
		colores[i] = sin(rad) * a;
    }
}

/////
// Genera el Plasma
/////
// Funcion para hacer en Assembly
void generarPlasma(Color rgb)
{
	Uint8* image;
	int i, j, x;
	Uint8 index;


	horizontal_1 = 0;
	horizontal_0 = vel_horizontal_0;

	image = (Uint8*)screen->pixels;

	for (i = 0; i < screen->h; ++i)
	{
		vertical_0 = vel_vertical_0;
		vertical_1 = 1;
		
		if (horizontal_0 >= 512) horizontal_0 = horizontal_0 % 512;
		if (horizontal_1 >= 512) horizontal_1 = horizontal_1 % 512;
		/*horizontal_0 &= 511;
		horizontal_1 &= 511;*/

		for (j = 0; j < screen->w; ++j)
		{
			if (vertical_0 >= 512) vertical_0 = vertical_0 % 512;
			if (vertical_1 >= 512) vertical_1 = vertical_1 % 512;
			/*vertical_0 &= 511;
			vertical_1 &= 511;*/

			x = colores[vertical_0] + colores[horizontal_0] + colores[vertical_1] + colores[horizontal_1];
			index = 128 + (x >> 4); 
			
			{
				if (index < 64)
				{
					*image++ = 255 - ( (index << 2) + 1 );
					*image++ = index << 2;
					*image++ = 0;
					*image++ = 1;
				}
				if (index >= 64 && index < 128)
				{
					*image++ = (index << 2) + 1;
					*image++ = 255;
					*image++ = 0;
					*image++ = 1;
				}
				if (index >= 128 && index < 192)
				{
					*image++ = 255 - ((index << 2) + 1);
					*image++ = 255 - ((index << 2) + 1);
					*image++ = 0;
					*image++ = 1;
				}
				if (index >= 192 && index < 256)
				{
					*image++ = (index << 2) + 1;
					*image++ = 0;
					*image++ = 0;
					*image++ = 1;
				}
				if (index >= 256)
				{
					*image++ = 0;
					*image++ = 0;
					*image++ = 0;
					*image++ = 1;
				}
			}

			vertical_0 += 1; 
			vertical_1 += 1; 
		}

		horizontal_1 += 1;
		horizontal_0 += 1;
	}
	vel_vertical_0 +=4;
	vel_horizontal_0 +=5;
}

/////
// Funcion Smooth para difuminar el escenario cuando cambia
/////
// Funcion para hacer en Assembly
bool negative(Uint8* screen, Uint32 w, Uint32 h)
{
	Uint32 negros = 0, blancos = 0;

	Uint8* arriba;
	Uint8* abajo;
	Uint8* adelante;
	Uint8* atras;
	
	Uint8 cero = 0;


	for (int i = 0; i < (int)h; i++) //2
	{
		for (int j = 0; j < (int)w*4; j++)  //6
		{
			((i > 0)? (arriba = screen - w*4) : (arriba = &cero));
			((i < (int)h-1)? (abajo = screen + w*4) : (abajo = &cero));
			((j < (int)w*4 - 4)? (adelante = screen + 4) : (adelante = &cero));
			((j > 3)? (atras = screen - 4) : (atras = &cero));
			
			double ar = (double)(*arriba)/(double)255.0;
			double ab = (double)(*abajo)/(double)255.0;
			double at = (double)(*atras)/(double)255.0;
			double ad = (double)(*adelante)/(double)255.0;

			// Contador para saber cuando termina el efecto
			//if (*arriba == 0 && *abajo == 0 && *adelante == 0 && *atras == 0){ negros++;}else{blancos++;}
			
			//*screen = ((*arriba) + (*abajo) + (*adelante) + (*atras))/4;
			
			double res = (1/sqrt((ar + ab + at + ad + 1)))*255;
			*screen = (Uint8)res;
			
			screen++;
		}
	}
	return negros > blancos;
}


void diagonal_C(Uint8* foto, Uint32 w, Uint32 r)
{
	Uint8* pixel = foto;

	for (int i = 0; i < (int)w; i++)
	{
		*pixel = 255;
		*(pixel+1) = 0;
		*(pixel+2) = 255;

		pixel += (w+1)*3;
	}

	pixel = foto;
	
	for (int i = 0; i < (int)r; i++)
	{
		pixel = foto + 3*i;
		for (int j = 0; j < (int)w-(int)r; j++)
		{
			*pixel = 255;
			*(pixel+1) = 0;
			*(pixel+2) = 255;
			
			pixel += (w+1)*3;
		}
		pixel = foto + w*3*i;

		for (int j = 0; j < (int)w-(int)r; j++)
		{
			*pixel = 255;
			*(pixel+1) = 0;
			*(pixel+2) = 255;
			
			pixel += (w+1)*3;
		}
	}
}

void diagonalInvertida_C(Uint8* foto, Uint32 w, Uint32 r)
{
	Uint8* pixel = foto + (w-1)*3;

	for (int i = 0; i < (int)w; i++)
	{
		*pixel = 255;
		*(pixel+1) = 0;
		*(pixel+2) = 255;

		pixel += (w-1)*3;
	}

	pixel = foto + (w-1)*3;
	
	for (int i = 0; i < (int)r; i++)
	{
		pixel = foto - 3*i;
		for (int j = 0; j < (int)w-(int)r; j++)
		{
			*pixel = 255;
			*(pixel+1) = 0;
			*(pixel+2) = 255;
			
			pixel += (w-1)*3;
		}
		pixel = foto + w*3*i;
		for (int j = 0; j < (int)w-(int)r; j++)
		{
			*pixel = 255;
			*(pixel+1) = 0;
			*(pixel+2) = 255;
			
			pixel += (w-1)*3;
		}
	}
}

//////
// Main Principal
/////
int main(int argc, char *argv[]) 
{
	//para medir tiempos de ejecucion
	Timer timerBlit;
	Timer timerCopiar;
	Timer timerCopiarI;
	Timer timerZoom;
	Timer timerPlasma;
	Timer timerNegative;	
	int cont = 0;
	int contP =0;
	int contZ =0;
	int contN =0;


	// Sprites Wolverine
	SDL_Surface *fondoP;

	// Manejo de Orientacion
	//bool sentido = true;

	// Sprites de Items
	SDL_Surface *hulkStand, *hulkStanding, *hulkZoom, *ironStand, *ironStanding ,*fight, *fightTMP, *lifebar, *lifebarT;
	
	SDL_Surface *ironTMP, *hulkTMP;
	
	//SDL_Rect playerOne, playerTwo, round, itemRec, life, hulkZ;
	SDL_Rect playerOne, playerTwo, round, life, hulkZ;
	
	// Sprites de Fondos
	//SDL_Surface *fondoActual;
	

	// Variables de Control de Tiempo para las Animaciones
	Uint32 ciclo = 0;
	Uint32 timePass = 0;
	Uint32 timePass2 = 0;
	Timer fps;

	// Manejo de Eventos de la SDL
	SDL_Event event;
	
	//bool biting = false;
	//int count_bit = 0;
	int done=0;

	Uint8 *keys;
	
	atexit(SDL_Quit);

	// Iniciar SDL
	if (SDL_Init(SDL_INIT_VIDEO) < 0)
	{
		printf("No se pudo iniciar SDL: %s\n",SDL_GetError());
		exit(1);
	}

	// Activamos modo de video
	screen = SDL_SetVideoMode(SCREEN_W,SCREEN_H,32,SDL_SWSURFACE);
	if (screen == NULL)
	{
		printf("No se puede inicializar el modo gr�fico: %s \n",SDL_GetError());
		exit(1);
	}
    // printf("The current video surface bits per pixel is %d\n", (int)screen->format->BitsPerPixel);

	fondoP = SDL_LoadBMP("imagenes/FondoP.bmp");
    if ( fondoP == NULL )
	{
        printf("No pude cargar gr�fico: %s\n", SDL_GetError());
        exit(1);
    }


	hulkStand = SDL_LoadBMP("imagenes/HulkStand.bmp");
	if ( hulkStand == NULL )
	{
		printf("No pude cargar gr�fico: %s\n", SDL_GetError());
        exit(1);
	}

	hulkTMP = SDL_LoadBMP("imagenes/HulkStand.bmp");
	if ( hulkTMP == NULL )
	{
		printf("No pude cargar gr�fico: %s\n", SDL_GetError());
        exit(1);
	}

	hulkStanding = SDL_LoadBMP("imagenes/HulkStanding.bmp");
	if ( hulkStanding == NULL )
	{
		printf("No pude cargar gr�fico: %s\n", SDL_GetError());
        exit(1);
	}

	ironStand = SDL_LoadBMP("imagenes/IronStand.bmp");
	if ( ironStand == NULL )
	{
		printf("No pude cargar gr�fico: %s\n", SDL_GetError());
        exit(1);
	}

	ironTMP = SDL_LoadBMP("imagenes/IronStand.bmp");
	if ( ironTMP == NULL )
	{
		printf("No pude cargar gr�fico: %s\n", SDL_GetError());
        exit(1);
	}

	ironStanding = SDL_LoadBMP("imagenes/IronStanding.bmp");
	if ( ironStanding == NULL )
	{
		printf("No pude cargar gr�fico: %s\n", SDL_GetError());
        exit(1);
	}

	fight = SDL_LoadBMP("imagenes/Fight.bmp");
	if ( fight == NULL )
	{
		printf("No pude cargar gr�fico: %s\n", SDL_GetError());
        exit(1);
	}

	fightTMP = SDL_LoadBMP("imagenes/Fight.bmp");
	if ( fightTMP == NULL )
	{
		printf("No pude cargar gr�fico: %s\n", SDL_GetError());
        exit(1);
	}

	lifebar = SDL_LoadBMP("imagenes/LifeBar.bmp");
	if ( lifebar == NULL )
	{
		printf("No pude cargar gr�fico: %s\n", SDL_GetError());
        exit(1);
	}

	lifebarT = SDL_LoadBMP("imagenes/LifeBar.bmp");
	if ( lifebarT == NULL )
	{
		printf("No pude cargar gr�fico: %s\n", SDL_GetError());
        exit(1);
	}

	hulkZoom = SDL_LoadBMP("imagenes/HulkZoom.bmp");
	if ( hulkZoom == NULL )
	{
		printf("No pude cargar gr�fico: %s\n", SDL_GetError());
        exit(1);
	}

	playerOne.x = 150;
	playerOne.y = 245;

	playerTwo.x = 550;
	playerTwo.y = 235;

	hulkZ.x = 430;
	hulkZ.y = 135;
	
	int a = 1024;
	

	round.x = 310;
	round.y = 70;

	//bool cambio_escenario = true;

	int ratio = 1;
	int ratio2 = 0;
	// Comenzamos el Ciclo del Juego
	while(done == 0)
	{
		
		fps.start();

		while (SDL_PollEvent(&event)) 
		{	
			if (event.type == SDL_QUIT) {done=1;}
					
			if (event.type == SDL_KEYDOWN || event.type == SDL_JOYBUTTONDOWN) {
						
				if (event.key.keysym.sym == SDLK_ESCAPE) {
					done=1;
				} 
			}
		}

		// Leyendo la entrada
		keys = SDL_GetKeyState(NULL);


		// Mostramos todo
		SDL_Flip(screen);
		

		// Generamos el fondo

		if(cont==0)
			timerCopiar.start();
		else
			timerCopiar.unpause();		
		
		copiar_C((Uint8*)fondoP->pixels, screen->w*4, screen->h, (Uint8*)screen->pixels);		
		timerCopiar.pause();
		if(cont==100){
			int tiempoC;
			tiempoC = timerCopiar.get_ticks();
			cout<<"tiempo de copiar: "<<tiempoC<<endl;		
		}


		
		Color color_off;
		Color color_off_sprites;

		color_off_sprites.r = 255;
		color_off_sprites.b = 255;
		color_off_sprites.g = 0;
		color_off_sprites.a = 1;

		color_off.r = 0;
		color_off.g = 0;
		color_off.b = 0;
		color_off.a = 1;

		// Fondo de Plasma
		init(a);
		if (a < 8192) a++;
		if (a == 8192) a = 1024;

		if (keys[SDLK_p]){ 
			if (contP ==0)			
				timerPlasma.start();
			else
				timerPlasma.unpause();			
			generarPlasma(color_off);
			timerPlasma.pause();
			if(contP==100){
				cout<<"tiempo de plasma: "<<timerPlasma.get_ticks()<<endl;
			}
			contP++;		
		}
		//if (keys[SDLK_t)) std::
		
		recortar(hulkStanding->pixels, ciclo%8, hulkStand->w, hulkStanding->w, hulkStanding->h, hulkStand->pixels, true);
		ratio = 0;

		if (!keys[SDLK_SPACE]) blit((Uint8*)screen->pixels, screen->w, screen->h, (Uint8*)hulkStand->pixels, hulkStand->w, hulkStand->h, playerTwo.x, playerTwo.y, color_off_sprites);
		if (!keys[SDLK_SPACE]) copiar_C((Uint8*)hulkStand->pixels, hulkStand->w*4, hulkStand->h, (Uint8*)hulkTMP->pixels);
		if (!keys[SDLK_SPACE]) SDL_BlitSurface(hulkTMP, NULL, screen, &playerTwo);
		



//mido el tiempo de zoom
		if (keys[SDLK_SPACE]){ 
			
			if (contZ ==0)			
				timerZoom.start();
			else
				timerZoom.unpause();			
			zoom_C((Uint8*)hulkStand->pixels, hulkStand->w*4, hulkStand->h, (Uint8*)hulkZoom->pixels);
			timerZoom.pause();
			if(contZ==100){
				cout<<"tiempo de zoom: "<<timerZoom.get_ticks()<<endl;
			}
			contZ++;		
			
		}




		if (keys[SDLK_SPACE]) blit((Uint8*)screen->pixels, screen->w, screen->h, (Uint8*)hulkZoom->pixels, hulkZoom->w, hulkZoom->h, hulkZ.x, hulkZ.y, color_off_sprites);
		if (keys[SDLK_SPACE]) SDL_BlitSurface(hulkZoom, NULL, screen, &hulkZ);
		
		recortar(ironStanding->pixels, ciclo%7, ironStand->w, ironStanding->w, ironStanding->h, ironStand->pixels, true);
		ratio = 0;		
		
		//mido el tiempo de copiar invertido		
		if(cont==0)
			timerCopiarI.start();
		else
			timerCopiarI.unpause();		
		
		copiarInvertido_C((Uint8*)ironStand->pixels, ironTMP->w*4, ironTMP->h, (Uint8*)ironTMP->pixels);		
		timerCopiarI.pause();
		if(cont==100){
			int tiempoC;
			tiempoC = timerCopiarI.get_ticks();
			cout<<"tiempo de copiar invertido: "<<tiempoC<<endl;		
		}

		//mido el tiempo de blit
		if(cont==0)
			timerBlit.start();
		else
			timerBlit.unpause();		
		blit((Uint8*)screen->pixels, screen->w, screen->h, (Uint8*)ironTMP->pixels, ironTMP->w, ironTMP->h, playerOne.x, playerOne.y, color_off_sprites);
		timerBlit.pause();
		if(cont==100){
			int tiempo;
			tiempo = timerBlit.get_ticks();
			cout<<"tiempo de blit: "<<tiempo<<endl;		
		}
		SDL_BlitSurface(ironTMP, NULL, screen, &playerOne);

		

		life.x = 20;
		life.y = 20;

		copiar_C((Uint8*)lifebar->pixels, lifebar->w*4, lifebar->h, (Uint8*)lifebarT->pixels);

		blit((Uint8*)screen->pixels, screen->w, screen->h, (Uint8*)lifebarT->pixels, lifebarT->w, lifebarT->h, life.x, life.y, color_off_sprites);

		SDL_BlitSurface(lifebarT, NULL, screen, &life);
		life.x = 530;
		life.y = 20;

		copiarInvertido_C((Uint8*)lifebar->pixels, lifebar->w*4, lifebar->h, (Uint8*)lifebarT->pixels);
		blit((Uint8*)screen->pixels, screen->w, screen->h, (Uint8*)lifebarT->pixels, lifebarT->w, lifebarT->h, life.x, life.y, color_off_sprites);

		SDL_BlitSurface(lifebarT, NULL, screen, &life);

		
		if (keys[SDLK_UP]) diagonal_C((Uint8*)fight->pixels, fight->w, ratio2%140);
		if (keys[SDLK_UP]) diagonalInvertida_C((Uint8*)fight->pixels, fight->w, ratio2%140);

		if (!keys[SDLK_UP]) copiar_C((Uint8*)fightTMP->pixels, fight->w*4, fight->h, (Uint8*)fight->pixels);
		if (!keys[SDLK_UP]) ratio2 = 0;

		
		//mido el tiempo de ive

		if (keys[SDLK_s]){
			
			if (contN ==0)			
				timerNegative.start();
			else
				timerNegative.unpause();			
			
			negative((Uint8*)screen->pixels, screen->w, screen->h);

			timerNegative.pause();
			if(contN==100){
				cout<<"tiempo de Negative: "<<timerNegative.get_ticks()<<endl;
			}
			contN++;		
		}

		



		blit((Uint8*)screen->pixels, screen->w, screen->h, (Uint8*)fight->pixels, fight->w, fight->h, round.x, round.y, color_off_sprites);
		SDL_BlitSurface(fight, NULL, screen, &round);
/**/
		int ticks = fps.get_ticks();
		
		if( ticks < 1000 / FRAMES_PER_SECOND )
        {
            SDL_Delay( ( 1000 / FRAMES_PER_SECOND ) - ticks );
        }
		if (timePass == 3) {ciclo++;timePass=0;}
		if (timePass2 == 3) {ratio2++;timePass2=0;}
		timePass++;timePass2++;
	cont++;	
   }
	
   return 0;
}

